﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MossmanMill.View;
using MossmanMill.DataModel;
using MossmanMill.Properties;
using System.Runtime.InteropServices;

namespace MossmanMill.RackPlanner
{
    public partial class RackPlanner : Form
    {

        private IDatabaseAccess db;
        private List<Card> inventoryCards;
        private List<NetworkCard> inventoryNCards;
        private List<Processor> inventoryPCards;
        private Slottable drag, selected;
        private StringFormat verticalText;
        private Rack rack;
        private MainForm mainform;

        public RackPlanner(MainForm form)
        {
            mainform = form;
            InitializeComponent();
            var dbFactory = new DBFactory();
            db = dbFactory.Make(DBFactory.CLASS_TYPE_DATABASE);
            inventoryCards = new List<Card>();
            foreach (var item in db.GetAvailableCards())
            {
                if (item.GetChannels() > 0 && item.Type != CardType.OTHER)
                {
                    CardListBox.Items.Add(item.Name);
                    inventoryCards.Add(item);
                }
            }
            var models = db.GetNetworkCardModels();
            inventoryNCards = new List<NetworkCard>(models.Count);
            foreach (var model in models)
            {
                inventoryNCards.Add(new NetworkCard(-1, -1, model, 'U', -1));
                NCardBox.Items.Add(model);
            }
            models = db.GetProcessorModels();
            inventoryPCards = new List<Processor>(models.Count); 
            foreach (var model in models)
            {
                inventoryPCards.Add(new Processor(-1, -1, model, -1));
                PCardBox.Items.Add(model);
            }
            verticalText = new StringFormat(StringFormatFlags.DirectionVertical);

            foreach (var item in db.GetRackModels())
            {
                RackModelField.Items.Add(item);
            }
            foreach (var item in db.GetRooms())
            {
                RackLocationField.Items.Add(item);
            }
            if (RackModelField.Items.Count > 0) RackModelField.SelectedIndex = 0;
            if (RackLocationField.Items.Count > 0) RackLocationField.SelectedIndex = 0;
            NCardConn.Items.Add("Server");
            foreach (var item in db.GetRacks())
            {
                NCardConn.Items.Add(item);
            }
            rack = new Rack(-1, -1, "", "");
            rack.Cards = new Slottable[13];
        }

        private Rack GenerateRack()
        {
            Room r = (Room) RackLocationField.SelectedItem;
            var room = r==null?-1:r.Id;
            Rack rack = new Rack(this.rack.Id, room, RackModelField.Text, RackAddressField.Text);
            Slottable[] modules = this.rack.Cards;
            var cards = rack.Cards;
            for (int i = 0; i < 13; i++)
            {
                if (cards[i] == null) continue;
                if (cards[i] != null) cards[i].Position = i;
                switch (cards[i].SlotType)
                {
                    case 'C':
                        var c = (Card)cards[i];
                        modules[i] = c;
                        break;
                    case 'N':
                        var n = (NetworkCard)cards[i];
                        modules[i] = n;
                        break;
                    case 'P':
                        var p = (Processor)cards[i];
                        modules[i] = p;
                        break;
                }
            }
            rack.SetCards(modules);
            RackPanel.Invalidate();
            return rack;
        }

        private void OnDraw(object sender, PaintEventArgs e)
        {
            rack.draw(e.Graphics, 10, 10, 1.0f, true);
        }

        private void PickCard(object sender, MouseEventArgs e)
        {
            Console.WriteLine("Pick Card");
            var lvi = CardListBox.GetItemAt(e.X, e.Y);
            if (lvi == null) return;
            var item = inventoryCards[lvi.Index];
            drag = item.Copy();
            var bmp = ((Card)drag).GetDrawBitmap(new Rectangle(0, 0, 33, 33), DrawType.Icon);
            DefaultCursor.Draw(Graphics.FromImage(bmp),new Rectangle(0,0,bmp.Width,bmp.Height));
            SetCursor(bmp);
            SplashPanel.Enabled = true;
        }

        private void SetCursor(Bitmap bmp)
        {
            if (bmp == null) Cursor = Cursors.Default;
            else Cursor = CreateCursor(bmp, 1, 1);
        }

        private void PickNCard(object sender, MouseEventArgs e)
        {
            //Console.WriteLine("Pick NCard");
            var lvi = NCardBox.GetItemAt(e.X, e.Y);
            if (lvi == null) return;
            var item = inventoryNCards[lvi.Index];
            drag = item.Copy();
            var bmp = ((NetworkCard)drag).GetDrawBitmap(new Rectangle(0, 0, 33, 33), DrawType.Icon);
            DefaultCursor.Draw(Graphics.FromImage(bmp), new Rectangle(0, 0, bmp.Width, bmp.Height));
            SetCursor(bmp);
            SplashPanel.Enabled = true;
        }

        private void PickPCard(object sender, MouseEventArgs e)
        {
            //Console.WriteLine("Pick PCard");
            var lvi = PCardBox.GetItemAt(e.X, e.Y);
            if (lvi == null) return;
            var item = inventoryPCards[lvi.Index];
            drag = item.Copy();
            var bmp = ((Processor)drag).GetDrawBitmap(new Rectangle(0, 0, 33, 33), DrawType.Icon);
            DefaultCursor.Draw(Graphics.FromImage(bmp), new Rectangle(0, 0, bmp.Width, bmp.Height));
            SetCursor(bmp);
            SplashPanel.Enabled = true;
        }

        private void DropItem(object sender, DragEventArgs e)
        {
            //TODO Maths might need to be changed here
            //Console.WriteLine(drag);
            //RackPanel.Invalidate();
            //if (drag == null) return;
            //var x = ((e.X - this.Location.X - RackPanel.Location.X - 88) / 40) % 13;
            //if (x < 0) return;
            //rack.Cards[x] = drag;
            //drag.Position = x;
            //if (drag.SlotType == 'P' && x < 12) rack.Cards[x + 1] = null;
            //selected = drag;
            
            //ShowAdditionalSettings();
            //rack = GenerateRack();
            
            //RackPanel.Invalidate();
            //Console.WriteLine("Drop Item");
            //SetCursor(null);
        }



        private void DropItem(object sender, MouseEventArgs e)
        {
            Console.WriteLine("Drop Item");
            if (drag == null) return;
            var x = ((e.X - 80) / 40) % 13;
            if (x < 0) return;
            drag.Position = x;
            rack.Cards[x] = drag;
            if (drag.SlotType == 'P' && x < 12) rack.Cards[x + 1] = null;
            selected = drag;
            ShowAdditionalSettings();
            rack = GenerateRack();
            SetCursor(null);
            drag = null;
        }

        private void DragEnterEvent(object sender, DragEventArgs e)
        {
            Console.WriteLine("Drag Enter");
            if (drag != null) e.Effect = DragDropEffects.Copy;
            RackPanel.Invalidate();
        }

        private void DrawCard(object sender, DrawListViewItemEventArgs e)
        {
            var g = e.Graphics;
            g.FillRectangle(e.ItemIndex % 2 == 0 ? Brushes.LightCyan : Brushes.PowderBlue, e.Bounds);
            g.DrawRectangle(Pens.Black, e.Bounds);
            g.DrawString(e.Item.Text, Fonts.smallCourier, Brushes.Black, e.Bounds.Left + 2, e.Bounds.Top + 6);
        }

        private void ClickRack(object sender, EventArgs e)
        {
            var args = (MouseEventArgs)e;
            rack.interact(args.Location, 2, 10, 10);
            selected = rack.SelectedCard;
            deleteCardBtn.Enabled = selected != null;
            ShowAdditionalSettings();
            RackPanel.Invalidate();
        }

        private void ShowAdditionalSettings()
        {
            bool showNCardSettings = false;
            bool showCardSettings = false;
            if (selected != null)
            {
                switch (selected.SlotType)
                {
                    case 'N':
                        showNCardSettings = true;
                        break;
                    case 'C':
                        showCardSettings = true;
                        break;
                }
            }
            NCardLbl.Visible = NCardPanel.Visible = showNCardSettings;
            ShowSensorPanel(showCardSettings);
        }

        private void ShowSensorPanel(bool show)
        {
            SensorPanel.Visible = show;
            SensorLbl.Visible = show;
            if (!show) return;
            var card = (Card)selected;
            ChannelSelect.setNumChannels(card.GetChannels());
            if (card.Sensors == null)
            {
                var sensors = new Sensor[card.GetChannels()];
                card.SetSensors(sensors);
            }
            else
            {
                for (int i = 0; i < card.GetChannels(); i++)
                {
                    ChannelSelect.setHasSensor(i, card.Sensors[i] != null);
                }
            }
        }

        private void SelectNCardType(object sender, EventArgs e)
        {
            switch (((RadioButton)sender).Text)
            {
                case "Upstream":
                    ((NetworkCard)selected).SetType('U');
                    break;
                case "Downstream":
                    ((NetworkCard)selected).SetType('D');
                    break;
            }
            RackPanel.Invalidate();
        }

        private void SelectNCardConn(object sender, EventArgs e)
        {
            var card = (NetworkCard)selected;
            card.SetConnection(NCardConn.SelectedIndex == 0 ? null : (Rack)NCardConn.SelectedItem);
        }

        private void AddSensor(object sender, EventArgs e)
        {
            var tag = sensorTag.Text;
            var param = sensorParam.Text;
            var desc = sensorDesc.Text;
            if (String.IsNullOrEmpty(tag) || String.IsNullOrEmpty(param) || String.IsNullOrEmpty(desc))
            {
                MessageBox.Show(Resources.FormValidationMsg);
                return;
            }

            var index = ChannelSelect.Channel;
            var card = (Card)selected;
            card.Sensors[index] = new Sensor(-1, tag, param, desc, index, card.CardId);
            ChannelSelect.setHasSensor(index, true);
            ChannelSelect.Invalidate();
        }

        private void SelectChannel(object sender, EventArgs e)
        {
            var index = ChannelSelect.Channel;
            var sensor = ((Card)selected).Sensors[index];
            deleteSensorBtn.Enabled = sensor != null;
            if (sensor == null)
            {
                sensorParam.Text = sensorTag.Text = sensorDesc.Text = "";
            }
            else
            {
                sensorTag.Text = sensor.Tag;
                sensorParam.Text = sensor.Param;
                sensorDesc.Text = sensor.Desc;
            }
        }

        public void LoadRack(Rack rack)
        {
            this.rack = rack;
            RackAddressField.Text = rack.Address;
            RackLocationField.Text = rack.Room.ToString();
            RackModelField.Text = rack.Model;
            RackPanel.Invalidate();
            if (rack.Id != -1) rackBrowserBtn.Enabled = true;
        }

        public void LoadSensor(Sensor sensor)
        {
            rack = sensor.Card.Rack;
            selected = sensor.Card;
            LoadCard(sensor.Card);
            ChannelSelect.Channel = sensor.Channel;
            sensorTag.Text = sensor.Tag;
            sensorParam.Text = sensor.Param;
            sensorDesc.Text = sensor.Desc;
            SensorPanel.Visible = true;
            RackPanel.Invalidate();
        }

        public void LoadCard(Card card)
        {
            rack = card.Rack;
            selected = card;
            rack.SelectedIndex = card.Position;
            SensorPanel.Visible = true;
            RackPanel.Invalidate();
        }

        private void CloseForm(object sender, EventArgs e)
        {
            this.Close();
        }

        private void SaveRack(object sender, EventArgs e)
        {
            IDatabaseAccess db = new DBFactory().Make(DBFactory.CLASS_TYPE_DATABASE);
            if (rack.Id == -1)
            {
                db.AddRack(rack);
            }
            else
            {
                Rack original = db.GetRack(rack.Id);
                if (original.Address.Equals(rack.Address) && original.Model.Equals(rack.Model) && original.RoomId == rack.RoomId) db.UpdateRack(rack); 
                else //If Address, Model, or Room has been changed make a new rack. DB Constraints
                {
                    rack.SetId(-1); //Prevent calls to rack.Cards to return database results if it is empty
                    db.AddRack(rack);
                }

            }
        }

        private void DeleteCard(object sender, EventArgs e)
        {
            var card = rack.SelectedCard;
            if (card == null) return;
            rack.Cards[card.Position] = null;
            RackPanel.Invalidate();
            if (card.SlotType == 'C') SensorPanel.Visible = false;
        }

        private void DeleteSensor(object sender, EventArgs e)
        {
            var card = rack.SelectedCard;
            if (card == null || card.SlotType != 'C') return;
            var sensor = ((Card)card).SelectedSensor;
            if (sensor == null) return;
            ((Card)card).Sensors[sensor.Channel] = null;
            ChannelSelect.Invalidate();
            sensorTag.Text = "";
            sensorParam.Text = "";
            sensorDesc.Text = "";
        }

        public struct IconInfo
        {
            public bool fIcon;
            public int xHotspot;
            public int yHotspot;
            public IntPtr hbmMask;
            public IntPtr hbmColor;
        }

        [DllImport("user32.dll")]
        public static extern IntPtr CreateIconIndirect(ref IconInfo icon);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);

        public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
        {
            IconInfo tmp = new IconInfo();
            GetIconInfo(bmp.GetHicon(), ref tmp);
            tmp.xHotspot = xHotSpot;
            tmp.yHotspot = yHotSpot;
            tmp.fIcon = false;
            return new Cursor(CreateIconIndirect(ref tmp));
        }

        private void CatchSplash(object sender, EventArgs e)
        {
            Console.WriteLine("Catch Splash");
            drag = null;
            SetCursor(null);
            SplashPanel.Enabled = false;
        }

        private void rackBrowserBtn_Click(object sender, EventArgs e)
        {
            if (rack.Id == -1) return;
            mainform.LoadRack(rack);
            mainform.Invalidate();
            mainform.Focus();
        }
    }
}
